﻿using System;
using System.Collections.Generic;

namespace Core
{
    /// <summary>
    /// 代码与Messenger基本相同
    /// 不同点在于此脚本可以挂载在Entity对象上，用于处理一个特定对象的相关事件
    /// </summary>
    public class EventComponent : Core.Component, IAwake
    {
        private readonly Dictionary<string, Delegate> eventTable = new();

        public void Awake()
        {

        }

        public override void Dispose()
        {
            if (IsDisposed) return;

            eventTable.Clear();

            base.Dispose();
        }

        #region Message logging and exception throwing
        private void OnListenerAdding(string eventType, Delegate listener)
        {
            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }
#if DEBUG
            Delegate d = eventTable[eventType];
            if (d != null && d.GetType() != listener.GetType())
            {
                log.err("有相同Key但不相同类型的监听添加->>>" + eventType);
            }
#endif
        }

        private void OnListenerRemoving(string eventType, Delegate listener)
        {
            if (eventTable.ContainsKey(eventType))
            {
                Delegate d = eventTable[eventType];
#if DEBUG
                if (d.GetType() != listener.GetType())
                {
                    log.err("有相同Key但不相同类型的监听删除->>>" + eventType);
                }
#endif
            }
            else
            {
                log.err("No Target Listener->>>" + eventType);
                eventTable.Add(eventType, null);
            }
        }

        private void OnListenerRemoved(string eventType)
        {
            if (eventTable[eventType] == null)
            {
                eventTable.Remove(eventType);
            }
        }

        #endregion


        #region AddListener
        public void AddListener(string eventType, Action handler)
        {
            if (IsDisposed) return;

            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Action)eventTable[eventType] + handler;
        }


        public void AddListener<T>(string eventType, Action<T> handler)
        {
            if (IsDisposed) return;

            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Action<T>)eventTable[eventType] + handler;
        }

        public void AddListener<T, U>(string eventType, Action<T, U> handler)
        {
            if (IsDisposed) return;

            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Action<T, U>)eventTable[eventType] + handler;
        }

        public void AddListener<T, U, V>(string eventType, Action<T, U, V> handler)
        {
            if (IsDisposed) return;

            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Action<T, U, V>)eventTable[eventType] + handler;
        }

        public void AddListener(Dictionary<string, Delegate> event_list)
        {
            if (IsDisposed) return;

            foreach (var pair in event_list)
            {
                OnListenerAdding(pair.Key, pair.Value);
                eventTable[pair.Key] = Delegate.Combine(eventTable[pair.Key], pair.Value);
            }
        }
        #endregion

        #region RemoveListener
        public void RemoveListener(string eventType, Action handler)
        {
            if (IsDisposed) return;

            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Action)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public void RemoveListener<T>(string eventType, Action<T> handler)
        {
            if (IsDisposed) return;

            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Action<T>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public void RemoveListener<T, U>(string eventType, Action<T, U> handler)
        {
            if (IsDisposed) return;

            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Action<T, U>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public void RemoveListener<T, U, V>(string eventType, Action<T, U, V> handler)
        {
            if (IsDisposed) return;

            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Action<T, U, V>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public void RemoveListener(Dictionary<string, Delegate> event_list)
        {
            if (IsDisposed) return;

            foreach (var pair in event_list)
            {
                OnListenerRemoving(pair.Key, pair.Value);
                eventTable[pair.Key] = Delegate.Remove(eventTable[pair.Key], pair.Value);
                OnListenerRemoved(pair.Key);
            }
        }
        #endregion


        private int currFrameCnt;
        private int postCnt;
        private bool CheckPost(string eventType)
        {
            if (currFrameCnt != UnityEngine.Time.frameCount)
            {
                currFrameCnt = UnityEngine.Time.frameCount;
                postCnt = 0;
            }
            postCnt++;

            if (postCnt > 0)
            {
                if (postCnt > 20)
                {
                    log.err($"估计死循环了 eventType={eventType} cnt={postCnt}");
                    return false;
                }
                //log.d("EventComponent", $"循环次数 state={state} cnt={m_switch_cnt}");
            }
            return true;
        }

        #region Broadcast
        public void Post(string eventType)
        {
            if (IsDisposed) return;
            if (!CheckPost(eventType)) return;

            if (eventTable.TryGetValue(eventType, out Delegate d))
            {
                (d as Action)?.Invoke();
            }
        }

        public void Post<T>(string eventType, T t)
        {
            if (IsDisposed) return;
            if (!CheckPost(eventType)) return;

            if (eventTable.TryGetValue(eventType, out Delegate d))
            {
                (d as Action<T>)?.Invoke(t);
            }
        }

        public void Post<T, U>(string eventType, T t, U u)
        {
            if (IsDisposed) return;
            if (!CheckPost(eventType)) return;

            if (eventTable.TryGetValue(eventType, out Delegate d))
            {
                (d as Action<T, U>)?.Invoke(t, u);
            }
        }

        public void Post<T, U, V>(string eventType, T t, U u, V v)
        {
            if (IsDisposed) return;
            if (!CheckPost(eventType)) return;

            if (eventTable.TryGetValue(eventType, out Delegate d))
            {
                (d as Action<T, U, V>)?.Invoke(t, u, v);
            }
        }
        #endregion

        #region Execute

        public void AddListener<TResult>(string eventType, Func<TResult> handler)
        {
            if (IsDisposed) return;

            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Func<TResult>)eventTable[eventType] + handler;
        }

        public void RemoveListener<TResult>(string eventType, Func<TResult> handler)
        {
            if (IsDisposed) return;

            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Func<TResult>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public TResult Execute<TResult>(string eventType)
        {
            if (IsDisposed) return default;
            if (!CheckPost(eventType)) return default;

            if (eventTable.TryGetValue(eventType, out Delegate d) == false)
            {
                return default;
            }
            if (d is not Func<TResult> func)
            {
                return default;
            }
            return func.Invoke();
        }

        public TResult Execute<T, TResult>(string eventType, T t)
        {
            if (IsDisposed) return default;
            if (!CheckPost(eventType)) return default;

            if (eventTable.TryGetValue(eventType, out Delegate d) == false)
            {
                return default;
            }
            if (d is not Func<T, TResult> func)
            {
                return default;
            }
            return func.Invoke(t);
        }

        public TResult Execute<T, U, TResult>(string eventType, T t, U u)
        {
            if (IsDisposed) return default;
            if (!CheckPost(eventType)) return default;

            if (eventTable.TryGetValue(eventType, out Delegate d) == false)
            {
                return default;
            }
            if (d is not Func<T, U, TResult> func)
            {
                return default;
            }
            return func.Invoke(t, u);
        }

        public TResult Execute<T, U, V, TResult>(string eventType, T t, U u, V v)
        {
            if (IsDisposed) return default;
            if (!CheckPost(eventType)) return default;

            if (eventTable.TryGetValue(eventType, out Delegate d) == false)
            {
                return default;
            }
            if (d is not Func<T, U, V, TResult> func)
            {
                return default;
            }
            return func.Invoke(t, u, v);
        }
        #endregion
    }
}
